package com.ccjk.beusoft.hot.plugin

import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.res.AssetManager
import android.content.res.Resources
import android.text.TextUtils
import android.widget.Toast
import com.ccjk.beusoft.plugin.host.BasePluginActivity
import com.ccjk.beusoft.plugin.host.PluginIntent
import dalvik.system.DexClassLoader
import java.io.File

@SuppressLint("StaticFieldLeak")
/**
 * Created by CCJK on 2017/12/2.
 */
object PluginManager {
    private lateinit var hostContext: Context
    private lateinit var dexOutDir: File
    private val pluginMap = HashMap<String, PluginPackage>(2)

    //
    fun setContext(context: Context) {
        hostContext = context.applicationContext
        dexOutDir = File(context.cacheDir, "pluginDex")
        if (!dexOutDir.isDirectory)
            dexOutDir.mkdirs()
    }

    fun loadApk(apkPath: String) {
        val packageInfo: PackageInfo = hostContext.packageManager.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES) ?: return
        val pluginPackageName = packageInfo.packageName
        if (pluginMap.containsKey(pluginPackageName)) return

        //1. get default activity class name
        var defaultActivityClassName = ""
        if (packageInfo.activities != null && packageInfo.activities.isNotEmpty()) {
            defaultActivityClassName = packageInfo.activities[0].name
        }

        //2. create plugin class loader
        val parentClassLoader = hostContext.classLoader      //不能用这个 ClassLoader.getSystemClassLoader()
        val pluginClassLoader = DexClassLoader(apkPath, dexOutDir.absolutePath, null, parentClassLoader)

        test(pluginClassLoader)
        //3. create plugin assetManager
        val assetManager = AssetManager::class.java.newInstance()
        val addAssetPath = assetManager::class.java.getMethod("addAssetPath", String::class.java)
        addAssetPath.isAccessible = true
        addAssetPath.invoke(assetManager, apkPath)

        //4. create plugin resources

        val resources = Resources(assetManager, hostContext.resources.displayMetrics, hostContext.resources.configuration)

        //5.cache plugin package
        pluginMap.put(pluginPackageName,
                PluginPackage(pluginPackageName, defaultActivityClassName, pluginClassLoader, packageInfo, resources, assetManager)
        )
    }


    private fun test(pluginClassLoader: ClassLoader) {
        try {
            pluginClassLoader.loadClass("com.ccjk.beusoft.plugin.host.BasePluginActivity")
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }


    fun getPluginPackage(packageName: String): PluginPackage? = pluginMap[packageName]


    fun startActivity(context: Context, intent: PluginIntent) {
        startPluginActivityForResult(context, intent, -1)
    }

    fun startPluginActivityForResult(context: Context, intent: PluginIntent, requestCode: Int) {
        val pluginPackage = pluginMap[intent.pluginPackageName]
        if (pluginPackage == null) {
            Toast.makeText(context, "找不到該插件", Toast.LENGTH_SHORT).show()
            return
        }
        val pluginActivityName = getActivityName(intent, pluginPackage)
        val pluginActivityClass = loadPluginClass(pluginPackage.pluginClassLoader, pluginActivityName)
        if (pluginActivityClass == null) {
            Toast.makeText(context, "找不到activity", Toast.LENGTH_SHORT).show()
            return
        }

//        if (!verifyPluginActivity(pluginActivityClass)) {
//            Toast.makeText(context, "插件Activity沒有繼承BasePluginActivity", Toast.LENGTH_SHORT).show()
//            return
//        }
        intent.putExtra(Const.KEY_PLUGIN_PACKAGE_NAME, intent.pluginPackageName)
        intent.putExtra(Const.KEY_PLUGIN_ACTIVITY_NAME, pluginActivityName)
        intent.setClass(context, HostProxyActivity::class.java)
        if (context is Activity) {
            context.startActivityForResult(intent, requestCode)
        } else {
            context.startActivity(intent)
        }
    }

    private fun loadPluginClass(classLoader: ClassLoader, className: String): Class<*>? {
        try {
            return Class.forName(className, true, classLoader)
        } catch (e: Exception) {

        }
        return null
    }

    private fun verifyPluginActivity(clazz: Class<*>): Boolean {
        if (BasePluginActivity::class.java.isAssignableFrom(clazz)) {
            return true
        }
        return false
    }


    private fun getActivityName(intent: PluginIntent, pluginPackage: PluginPackage): String {
        val activityName = if (TextUtils.isEmpty(intent.pluginComponentName))
            pluginPackage.defaultActivityClassName
        else intent.pluginComponentName
        return if (activityName.startsWith(".")) {
            intent.pluginPackageName + activityName
        } else activityName

    }


}